#include "DownloadTool.h"
#include "MachineHelper.h"
#include <QTimer>
#include <QStandardPaths>
#include <QDebug>
#include <QRandomGenerator>

const QString defaultFileName = "tmp";

// 废弃
#if 0
static QTime  glastErrTime    = QTime::currentTime();
// log output optimization
void logErrorMessage(const QString& errMessage)
{
    if (glastErrTime.secsTo(QTime::currentTime()) >= 20) {
        qDebug() << errMessage;
        glastErrTime = QTime::currentTime();
    }
}
#endif

DownloadTool::DownloadTool(const QString& downloadUrl, const QString& savePath, QObject* parent)
    : QObject(parent), m_downloadUrl(downloadUrl), m_savePath(savePath), m_httpRequestAborted(false)
{
    // 断网重连后重新下载
    connect(MachineHelper::GetInstance(), &MachineHelper::sigNetOnlineStatusChanged, this, [=](bool isOnline) {
        if (isOnline) {
            QTimer::singleShot(1000, this, [=]() { startDownload(); });
        }
    });
}

DownloadTool::~DownloadTool()
{
    delete m_reply;

    // 清除访问缓存
    m_qnam.clearAccessCache();
}

void DownloadTool::startDownload()
{
    const QUrl newUrl = QUrl::fromUserInput(m_downloadUrl);

    if (!newUrl.isValid()) {
#ifdef DOWNLOAD_DEBUG
        qDebug() << QString("Invalid URL: %1: %2").arg(m_downloadUrl, newUrl.errorString());
#endif  // DOWNLOAD_DEBUG
        return;
    }

    QString fileName = newUrl.fileName();
    if (fileName.isEmpty()) fileName = defaultFileName;

    if (m_savePath.isEmpty()) m_savePath = QStandardPaths::writableLocation(QStandardPaths::TempLocation);

    if (!QFileInfo(m_savePath).isDir()) {
        QDir dir;
        dir.mkpath(m_savePath);
    }

    fileName.prepend(m_savePath + '/');
    if (QFile::exists(fileName)) QFile::remove(fileName);

    m_file.reset(openFileForWrite(fileName));
    if (!m_file) {
        qDebug() << "#####startDownload, m_file is null";
        return;
    }

    m_fileName = fileName;
    startRequest(newUrl);
}

void DownloadTool::cancelDownload()
{
    m_httpRequestAborted = true;
    m_reply->abort();
}

void DownloadTool::httpFinished()
{
    qDebug() << "## Enter DownloadTool::httpFinished()";
    QFileInfo fi;
    if (m_file) {
        fi.setFile(m_file->fileName());
        m_file->close();
        m_file.reset();
    }

    if (m_httpRequestAborted) {
        qDebug() << "## m_httpRequestAborted:" << m_httpRequestAborted;
        return;
    }

    if (m_reply->error()) {
        QFile::remove(fi.absoluteFilePath());
#ifdef DOWNLOAD_DEBUG
        qDebug() << QString("Download failed: %1.").arg(m_reply->errorString());
        Q_EMIT sigDownloadFailed();
#endif  // DOWNLOAD_DEBUG
        return;
    }

    const QVariant redirectionTarget = m_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);

    if (!redirectionTarget.isNull()) {
        const QUrl redirectedUrl = m_url.resolved(redirectionTarget.toUrl());
        m_file.reset(openFileForWrite(fi.absoluteFilePath()));
        if (!m_file) {
            qDebug() << "Failed to open file for writing: " << fi.absoluteFilePath();
            return;
        }

        qDebug() << "DownloadTool::startRequest(), redirectedUrl:" << redirectedUrl;
        startRequest(redirectedUrl);
        return;
    }

    m_httpSuccessed = true;
    qDebug() << "##sigDownloadFinished,m_httpSuccessed:" << m_httpSuccessed;
    Q_EMIT sigDownloadFinished();

#ifdef DOWNLOAD_DEBUG
    qDebug() << QString(tr("Downloaded %1 bytes to %2 in %3")
                            .arg(fi.size())
                            .arg(fi.fileName(), QDir::toNativeSeparators(fi.absolutePath())));
    qDebug() << "##End httpFinished()";
#endif  // DOWNLOAD_DEBUG
}

void DownloadTool::httpReadyRead()
{
    if (m_file) {
        m_file->write(m_reply->readAll());
    }
}

void DownloadTool::networkReplyProgress(qint64 bytesRead, qint64 totalBytes)
{
    qreal progress = qreal(bytesRead) / qreal(totalBytes);
    Q_EMIT sigProgress(bytesRead, totalBytes, progress);

#ifdef DOWNLOAD_DEBUG
    qDebug() << QString::number(progress * 100, 'f', 2) << "%    " << bytesRead / (1024 * 1024) << "MB" << "/"
             << totalBytes / (1024 * 1024) << "MB";
#endif  // DOWNLOAD_DEBUG
}

void DownloadTool::startRequest(const QUrl& requestedUrl)
{
    qDebug() << "##Enter startRequest";
    m_url                = requestedUrl;
    m_httpRequestAborted = false;

    if (m_reply) delete m_reply;

    m_reply = m_qnam.get(QNetworkRequest(m_url));

    connect(m_reply, &QNetworkReply::finished, this, &DownloadTool::httpFinished);
    connect(m_reply, &QIODevice::readyRead, this, &DownloadTool::httpReadyRead);
    connect(m_reply, &QNetworkReply::downloadProgress, this, &DownloadTool::networkReplyProgress);

#ifdef DOWNLOAD_DEBUG
    qDebug() << QString(tr("Downloading %1...").arg(m_url.toString()));
#endif  // DOWNLOAD_DEBUG
}

QFile* DownloadTool::openFileForWrite(const QString& fileName)
{
    QFile* file = new QFile(fileName);
    if (!file->open(QIODevice::WriteOnly)) {
#ifdef DOWNLOAD_DEBUG
        qDebug()
            << QString("Unable to save the file %1: %2.").arg(QDir::toNativeSeparators(fileName), file->errorString());
#endif  // DOWNLOAD_DEBUG
        return nullptr;
    }

    return file;
}

QString DownloadTool::getFileName()
{
    return m_fileName;
}
bool DownloadTool::getDownloadSuccessFlag()
{
    return m_httpSuccessed;
}
QUrl DownloadTool::getgetDownloadUrl()
{
    return m_url;
}
